/*
    Copyright (C) 2011 Rodrigo Castaño - Hugo Arregui, FuDePAN

    This file is part of the Prot-Filer Library

    Prot-Filer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Prot-Filer is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Prot-Filer.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef READ_UTILS_H
#define READ_UTILS_H

#include <iostream>
#include <stdint.h>
#include <netinet/in.h>
#include <mili/mili.h>
#include "prot-filer/definitions.h"
#include "prot-filer/angles.h"

declare_static_assert(size_equals_32bits);

namespace prot_filer
{
using std::istream;
using std::ostream;
typedef uint32_t BufferType;
typedef unsigned int SizeInBits;

static const unsigned char END_OF_DATA = 0xff;

class CompressedHelper
{
public:
    static bool is_next_char_readable(istream& file)
    {
        file.get();
        const bool result = file.eof();
        file.unget();
        return result;
    }

    template <class T>
    static size_t sizeof_in_bits(T t)
    {
        return sizeof(t) << 3;
    }

    static unsigned int angle_id_size_in_bits(unsigned int number_of_angles_id)
    {
        return static_cast<unsigned int>(ceil(log2(double(number_of_angles_id))));
    }

    static unsigned int get_mapping_file_size_in_bytes(const AnglesMapping* angles_mapping);
    static unsigned int get_structure_file_size_in_bytes(const AnglesData* angles_data);
    static unsigned int get_last_position(istream& file, const AnglesMapping*, const AnglesData*);

    static AnglesMapping* read_mapping(istream& file);
    static void read(istream& file, AngleIdPair& angle_id_pair);

    static void write_mapping(ostream& file, const AnglesMapping& angles_mapping);
    template <class T> static void write_var(ostream& file, const T& var)
    {
        template_compile_assert(mili::template_info<T>::size == sizeof(uint32_t), size_equals_32bits);
        const uint32_t temp_var = htonl(reinterpret_cast<const uint32_t&>(var));
        const char* to_be_written = reinterpret_cast<const char*>(&temp_var);
        file.write(to_be_written, sizeof(var));
    }

    template <class T> static void read_var(istream& file, T& var)
    {
        template_compile_assert(mili::template_info<T>::size == sizeof(uint32_t) , size_equals_32bits);
        char* to_be_read = reinterpret_cast<char*>(&var);
        file.read(to_be_read, sizeof(var)) ;
        const uint32_t result = ntohl(reinterpret_cast<const uint32_t&>(var));
        var = reinterpret_cast<const T&>(result);
    }
};

struct CustomReadBuffer
{
    CustomReadBuffer(unsigned int number_of_angles_id, istream& file) ;
    void read_var(AngleId& var);
    void read(AngleIdPair& angle_id_pair);
private:
    const SizeInBits angles_id_size;
    inline void physical_read();
    BufferType read_buffer;
    unsigned int available_reads;
    istream& file;
    const unsigned int mask;
    const unsigned int reads_per_buffer;
    CompressedHelper helper;
};

struct CustomWriteBuffer
{
    CustomWriteBuffer(unsigned int number_of_angles_id, ostream& file);
    void write_var(AngleId var);
    void write(const AngleIdPair& angle_id_pair);
    void flush_buffer();
private:
    const SizeInBits angles_id_size;
    BufferType write_buffer;
    unsigned int available_writes;
    ostream& file;
    const unsigned int original_mask;
    unsigned int shifts;
    const unsigned int writes_per_buffer;
    CompressedHelper helper;
};

}
#endif
